package com.yellowpage.white.util;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author leeon
 * 对类路径上文件操作的一些工具类
 */
public class BeanUtil {

	private static Log logger = LogFactory.getLog(BeanUtil.class);
	
	
	/**
	 * 根据文件所在类路径返回对应的绝对路径的文件对象。
	 * 该文件的URL是类装载器在类路径上找到的第一个该路径上的文件的URL
	 * @param classPath 文件类路径
	 * @return 文件URL绝对路径对应的File对象
	 * @throws FileNotFoundException
	 * @throws UnsupportedEncodingException
	 * @throws URISyntaxException 
	 */
	public static File getClassPathFile(String classPath) throws FileNotFoundException {
		URL url = getClassPathFileURL(classPath);
		if (url != null && "file".equals(url.getProtocol()))
		try {
			return new File(url.toURI());
		
		//toURI如果有空格，可能会抛出错误，所以需要进行另外的处理
		} catch (URISyntaxException e) {
			logger.warn("url to URI syntax exception, maybe jdk bug");
			logger.warn(e);
			
			String p = url.getFile();
			if (p.charAt(0) == '/' && p.charAt(2) == ':') p = p.substring(1);
			return new File(p);
		}
		else if (url != null)
			throw new FileNotFoundException("this file [" + url + "] in class path:[" + classPath + "] not a filesystem file");
		else
			throw new FileNotFoundException("this file not in class path:["+ classPath + "]");
	}

	/**
	 * 根据文件所在类路径返回该文件对应的绝对URLs。
	 * 该URL数组是类装载器在类路径上找到的所有该路径文件的URL的集合
	 * URL格式可能是file:/D:/foo/bar/classes/com/guanghua/brick/aaa.class
	 * 也有可能是jar:file:/D:/foo/bar/brick.jar!com/guanghua/brick/aaa.class等等
	 * @param classPath 文件类路径
	 * @return 文件所在绝对路径URL的数组
	 * @throws IOException 
	 */
	public static URL[] getClassPathFileURLs(String classPath) throws IOException {
		Enumeration e = Thread.currentThread().getContextClassLoader().getResources(classPath);
		
		//将url放入hashset以去掉重复的url
		HashSet<URL> set = new HashSet<URL>();
		while (e.hasMoreElements()) {
			Object o = e.nextElement();
			set.add((URL)o);
			logger.debug("find file [" + classPath +"] from class path, and url is [" + o + "]");
		}
		
		return (URL[]) set.toArray(new URL[0]);
	}

	/**
	 * 根据文件所在类路径返回该文件对应的绝对URL。
	 * 该URL是类装载器在类路径上找到的第一个该路径上的文件的URL
	 * URL格式可能是file:/D:/foo/bar/classes/com/guanghua/brick/aaa.class
	 * 也有可能是jar:file:/D:/foo/bar/brick.jar!com/guanghua/brick/aaa.class等等
	 * @param classPath 文件类路径
	 * @return 文件所在绝对路径URL
	 * @throws Exception
	 */
	public static URL getClassPathFileURL(String classPath) {
		return Thread.currentThread().getContextClassLoader().getResource(classPath);
	}

	/**
	 * 根据文件所在类路径返回对应文件的输入流。
	 * @param classPath 文件类路径
	 * @return 该文件的输入流
	 */
	public static InputStream getClassPathFileByInputStream(String classPath) {
		return Thread.currentThread().getContextClassLoader().getResourceAsStream(classPath);
	}
	
	
	/**
	 * use BeanUtil to get property of bean
	 * it will return null when property is user.account.name and user.account is null
	 * @param o, the bean instance
	 * @param property, the property of beans
	 * @return the obj get by property
	 */
	public static Object getBeanProperty(Object o, String property) {
		if (o == null || property == null) return null;
		String[] properties = property.split("\\.");
		for (int i = 0; i < properties.length; i ++) {
			try {
				o = PropertyUtils.getProperty(o, properties[i]);
				if (o == null) return null;
			} catch (Exception e) {
				logger.error("get bean property error", e);
				return null;
			}			
		}		
		return o;
	}
	
	/**
	 * use BeanUtil to set property of bean
	 * it will return create new obj account when property is user.account.name
	 * @param o, the bean instance
	 * @param property, the property of beans
	 */
	public static void setBeanProperty(Object o, String property, Object v) {
		if (o == null || v == null || property == null) return;
		String[] properties = property.split("\\.");
		try {
			for (int i = 0; i < properties.length; i ++) {			
				Object obj = o;
				if (i != properties.length - 1) {
					o = PropertyUtils.getProperty(obj, properties[i]);
					if (o == null)
						o = PropertyUtils.getPropertyType(obj, properties[i]).newInstance();
				} else {
					o = v;	
				}
				PropertyUtils.setProperty(obj, properties[i], o);			
			}
		} catch (Exception e) {
			logger.error("set bean property error", e);
		}
	}
	
	/**
	 * 通过一个类，寻找其某个属性的返回值
	 * 比如通过user.sex.seDesc的方式来获取
	 * @param clazz 类名
	 * @param property 属性
	 * @return 属性类型
	 */
	public static Class<?> getBeanPropertyType(Class<?> clazz, String property) {
		int e = property.indexOf('.');
		//最后一段
		if (e == -1) {
			return getPropertyType(clazz, property);
		//截取第一段，剩下递归
		} else {
			String p = property.substring(0, e);
			Class c = getPropertyType(clazz, p);
			if (c == null) return null;
			else return getBeanPropertyType(c, property.substring(e+1));
		}
	}
	
	/**
	 * 通过一个类，寻找其某个属性的返回值
	 * 比如通过seDesc的方式来获取，不能通过.来递归
	 * @param clazz 类名
	 * @param property 属性
	 * @return 属性类型
	 */
	
	public static Class<?> getPropertyType(Class<?> clazz, String property) {
		PropertyDescriptor pd = getPropertyDescriptor(clazz, property);
		if (pd != null) return pd.getPropertyType();
		else return null;
	}
	
	
	/**
	 * 通过一个类，寻找其某个属性的pd
	 * 比如通过user.sex.seDesc的方式来获取
	 * @param clazz 类名
	 * @param property 属性
	 * @return 属性的pd
	 */
	public static PropertyDescriptor getBeanPropertyDescriptor(Class<?> clazz, String property) {
		int e = property.indexOf('.');
		//最后一段
		if (e == -1) {
			return getPropertyDescriptor(clazz, property);
		//截取第一段，剩下递归
		} else {
			String p = property.substring(0, e);
			Class c = getPropertyType(clazz, p);
			if (c == null) return null;
			else return getPropertyDescriptor(c, property.substring(e+1));
		}

	}
	
	/**
	 * 通过一个类，寻找其某个属性的Pd
	 * @param clazz 类名
	 * @param property 属性
	 * @return 属性的pd
	 */
	public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String property) {
		for (PropertyDescriptor pd : PropertyUtils.getPropertyDescriptors(clazz)) {
			if (pd.getName().equals(property)) return pd;
		}
		return null;
	}
	
	/**
	 * 根据geter和seter，获取上面配置的annotation
	 * @param <T> annotation的类型
	 * @param setter
	 * @param getter
	 * @param annotationClass
	 * @return 返回配置的annotation实例
	 */
	public static <T extends Annotation> T getPropertyAnnotation(Method setter, Method getter, Class<T> annotationClass) {
		if (getter != null && getter.getAnnotation(annotationClass) != null) {
			return getter.getAnnotation(annotationClass);
		} else if (setter != null && setter.getAnnotation(annotationClass) != null) {
			return setter.getAnnotation(annotationClass);
		} else {
			return null;
		}
	}

}
